package work.chenbo.spring_boot_shiro.config.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import work.chenbo.spring_boot_shiro.config.properties.ShiroProperties;

import java.util.ArrayList;
import java.util.List;

/**
 * 授权配置
 * @author ChenBo
 * @className ShiroConfig
 * @date 2019/10/25
 */
@SpringBootConfiguration
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroConfig {


    @Autowired
    private ShiroProperties properties;

    /**
     * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
     * 在@Controller注解的类的方法中加入@RequiresRole注解，会导致该方法无法映射请求，导致返回404。
     * 加入这项配置能解决这个bug
     * @author ChenBo
     * @date 2019/10/25
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setUsePrefix(true);
        return creator;
    }

    /**
     * 权限管理，配置主要是Realm的管理认证
     * @author ChenBo
     * @date 2019/10/25
     */
    @Bean("firstRealm")
    public Realm firstRealm(){
        ShiroRealm shiroRealm = new ShiroRealm();
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 使用md5 算法进行加密
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 设置散列次数： 意为加密几次
        hashedCredentialsMatcher.setHashIterations(1024);
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return shiroRealm;
    }


    /**
     * 权限管理，配置主要是Realm的管理认证
     * @author ChenBo
     * @date 2019/10/25
     */
    @Bean("secondRealm")
    public Realm secondRealm(){
        ShiroRealm shiroRealm = new ShiroRealm();
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 使用md5 算法进行加密
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 设置散列次数： 意为加密几次
        hashedCredentialsMatcher.setHashIterations(1024);
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return shiroRealm;
    }

    /**
     *
     * @author ChenBo
     * @date 2019/10/28
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthenticator(modularRealmAuthenticator());
        securityManager.setRealms(new ArrayList<Realm>(){{
            add(firstRealm());
            add(secondRealm());
        }});
        return securityManager;
    }


    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(){
        ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
        AuthenticationStrategy authenticationStrategy = new AtLeastOneSuccessfulStrategy();
        modularRealmAuthenticator.setAuthenticationStrategy(authenticationStrategy);
        return modularRealmAuthenticator;
    }

    /**
     * 设置对应的过滤条件和跳转条件
     * @author ChenBo
     * @date 2019/10/25
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        List<String> ignorePaths = properties.getIgnorePaths();
        if(!ignorePaths.isEmpty()){
            ignorePaths.forEach(path->{
                chainDefinition.addPathDefinition(path,"anon");
            });
        }

        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }

    /**
     * 配置Thymeleaf整合shiro
     * @author ChenBo
     * @date 2019/10/28
     */
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
}
